package tools;


import systems.exceptions.equipments.ItemIsNullException;
import tools.Visitor;
import characters.Characters;
import equipments.*;
import equipments.enumeration.AccessoriesSlots;
import equipments.enumeration.ArmorSlots;
import equipments.enumeration.WeaponsType;


/**
 * This class regroup every visitor for items
 * @author Benjamin ORMAN & Baptiste LEGRAND
 * */

public class ItemVisitor implements Visitor {
	private Characters user; /** @param hero is a character. It's the character who have got the visited object.*/
	private boolean isMainHand;
	private enum bonusType{
	  maxhp,
	  maxmp,
	  hitaccuracy,
	  dodge,
	  phyattack,
	  magicalskillboost,
		parry,
		block,
		critical,
		magicalhitaccuracy,
		magicalresist,
		attackdelay,
		boosthate,
		boostcastingtime,
		concentration,
		physicaldefend,
		speed,
		maxfp,
		elementaldefendwater,
		elementaldefendfire,
		elementaldefendwind,
		elementaldefendearth,
		magicalcritical,
		magicalattack,
		pvpattackratio,
		pvpdefendratio,
		flyspeed
		};
	private enum WeaponTypeCorrespondance{
		  one_h_dagger,
		  one_h_mace,
		  one_h_sword,
		  one_h_toolhoe,
		  two_h_book,
		  two_h_orb,
		  two_h_polearm,
		  two_h_staff,
		  two_h_sword,
		  two_h_toolpick,
		  two_h_toolrod
		  ,bow		
	};
	
	/**
	 * The constructor
	 * @param h is the character
	 * */
	public ItemVisitor(Characters h)
	{
		this.user=h;
		this.isMainHand=false;
	}

	
	/**
	 * This method do nothing because we don't directly use the type Item.
	 */
	public void visit(Items item) {}


	public void visit(Manastone mana)
	{
		this.addBonus(mana.getBonus());
	}
	
	public void visit(Weapon weapon) 
  {
		WeaponsType mainType=null;
		WeaponsType secondType=null;
		try {
			if(this.user.getEquipment().getMainHandItem()==weapon)
			{
				this.isMainHand=true;
			}
} catch (ItemIsNullException e) {}
if(this.isMainHand==true)
{
	mainType=weapon.getType();
	this.user.getStat().setMainHandAttack((int)(((weapon.getMin_damage()+weapon.getMax_damage())/2)*this.user.getPower()/100.0));
    this.user.getStat().setMainHandCritRate(this.user.getMainHandCritRate()+weapon.getPhysical_crit());
    this.user.getStat().setMainHandAccuracy(this.user.getMainHandAccuracy()+weapon.getHit_accuracy());
    try {
		if(this.user.getEquipment().getOffHandItem()==null)
		{
			this.user.getStat().setAttackSpeed(weapon.getAttack_speed());
		}
		else
		{
			switch(((Weapon) this.user.getEquipment().getOffHandItem()).getType())
			{
				case one_h_dagger:
					secondType=WeaponsType.one_h_dagger;
					break;
				case one_h_sword:
					secondType=WeaponsType.one_h_sword;
					break;
				default:break;
			}
			if(mainType==WeaponsType.one_h_dagger && secondType==WeaponsType.one_h_dagger)
				this.user.getStat().setAttackSpeed(1.5);
			if(mainType==WeaponsType.one_h_dagger && secondType==WeaponsType.one_h_sword)
				this.user.getStat().setAttackSpeed(1.55);
			if(mainType==WeaponsType.one_h_sword && secondType==WeaponsType.one_h_dagger)
				this.user.getStat().setAttackSpeed(1.55);
			if(mainType==WeaponsType.one_h_sword && secondType==WeaponsType.one_h_sword)
				this.user.getStat().setAttackSpeed(1.7);
		}
	} catch (ItemIsNullException e){this.user.getStat().setAttackSpeed(weapon.getAttack_speed());}
    
}
else
{
	secondType=weapon.getType();
	this.user.getStat().setOffHandAttack((int)(((weapon.getMin_damage()+weapon.getMax_damage())/2)*this.user.getPower()/100.0));
    this.user.getStat().setOffHandCritRate(this.user.getoffHandCritRate()+weapon.getPhysical_crit());
    this.user.getStat().setOffHandAccuracy(this.user.getOffHandAccuracy()+weapon.getHit_accuracy());
    try {
		if(this.user.getEquipment().getMainHandItem()==null)
		{
			this.user.getStat().setAttackSpeed(weapon.getAttack_speed());
		}
		else
		{
			switch(((Weapon) this.user.getEquipment().getMainHandItem()).getType())
			{
				case one_h_dagger:
					mainType=WeaponsType.one_h_dagger;
					break;
				case one_h_sword:
					mainType=WeaponsType.one_h_sword;
					break;
				default:break;
			}
			if(mainType==WeaponsType.one_h_dagger && secondType==WeaponsType.one_h_dagger)
				this.user.getStat().setAttackSpeed(1.5);
			if(mainType==WeaponsType.one_h_dagger && secondType==WeaponsType.one_h_sword)
				this.user.getStat().setAttackSpeed(1.55);
			if(mainType==WeaponsType.one_h_sword && secondType==WeaponsType.one_h_dagger)
				this.user.getStat().setAttackSpeed(1.55);
			if(mainType==WeaponsType.one_h_sword && secondType==WeaponsType.one_h_sword)
				this.user.getStat().setAttackSpeed(1.7);
		}
	} catch (ItemIsNullException e){this.user.getStat().setAttackSpeed(weapon.getAttack_speed());}
}
 	//this.user.getStat().setAttackSpeed(weapon.getAttack_speed());
 	this.user.getStat().setMagicAccuracy(this.user.getMagicAccuracy()+weapon.getMagic_accuracy());
    this.user.getStat().setParry(this.user.getParry()+weapon.getParry());
    
    for(int i=0;i<weapon.getBonusList().size();i++)
      this.addBonus(weapon.getBonusList().get(i));
    
    for(int j=0;j<this.user.getMasteryList().size();j++)
    {
      if(this.user.getMasteryList().get(j).getCarac().equals(this.correspondance(weapon.getType())))
      {
    	  if(this.isMainHand==true)
    	  {
        int BonustoAdd =(int)(this.user.getMainHandAttack()*(this.user.getMasteryList().get(j).getValue()/100.0));
        this.user.getStat().setBonusHandAttack(this.user.getBonusMainHandAttack()+BonustoAdd);
    	  }
    	  else
    	  {
    	        int BonustoAdd =(int)(this.user.getOffHandAttack()*(this.user.getMasteryList().get(j).getValue()/100.0));
    	        this.user.getStat().setBonusOffHandAttack(this.user.getBonusOffHandAttack()+BonustoAdd); 
    	  }
      }
    }
    this.isMainHand=false;
  }
	
	public void visit(Armor armor)
	{
		this.user.getStat().setPhysicalDef(this.user.getPhysicalDef()+armor.getPhysical_def());
		this.user.getStat().setEvasion(this.user.getEvasion()+armor.getEvasion());
		this.user.getStat().setBlock(this.user.getBlock()+armor.getBlock());
		this.user.getStat().setMagicalResist(this.user.getMagicalResist()+armor.getMagical_resist());
		for(int i=0;i<armor.getBonusList().size();i++)
			this.addBonus(armor.getBonusList().get(i));
		try {
			if(this.user.getEquipment().getArmorPart(ArmorSlots.GLOVES)!=null &&
			   this.user.getEquipment().getArmorPart(ArmorSlots.JACKET)!=null &&
			   this.user.getEquipment().getArmorPart(ArmorSlots.PANTS)!=null &&
			   this.user.getEquipment().getArmorPart(ArmorSlots.PAULDRONS)!=null &&
			   this.user.getEquipment().getArmorPart(ArmorSlots.SHOES)!=null){
			this.user.getStat().setBonusPhyDef(this.user.getBonusPhyDef()+((int)(this.user.getPhysicalDef()*0.11)));
			}
		} catch (ItemIsNullException e) {}
	}

	public void visit(Accessory accessory)
	{
		this.user.getStat().setMagicalResist(this.user.getMagicalResist()+accessory.getMagical_resist());
		for(int i=0;i<accessory.getBonusList().size();i++)
			this.addBonus(accessory.getBonusList().get(i));
	}
	
	private void addBonus(BonusAttribut bonusAttribut) {
		switch(bonusType.valueOf(bonusAttribut.getCarac()))
		{
		case maxhp:
				this.user.getStat().setBonusHp(this.user.getBonusHp()+bonusAttribut.getValue());
				break;
		case maxmp:
				this.user.getStat().setBonusMp(this.user.getBonusMp()+bonusAttribut.getValue());
				break;
		case hitaccuracy:
			if(this.isMainHand==true)
			{
				this.user.getStat().setBonusHandAccuracy(this.user.getBonusMainHandAccuracy()+bonusAttribut.getValue());
				
			}
				else
				{
				this.user.getStat().setBonusOffHandAccuracy(this.user.getBonusOffHandAccuracy()+bonusAttribut.getValue());
				}
				break;
		case dodge:
				this.user.getStat().setBonusEvasion(this.user.getBonusEvasion()+bonusAttribut.getValue());
				break;
		case phyattack:
			if(this.isMainHand==true)
				this.user.getStat().setBonusHandAttack(this.user.getBonusMainHandAttack()+bonusAttribut.getValue());
			else
				this.user.getStat().setBonusOffHandAttack(this.user.getBonusOffHandAttack()+bonusAttribut.getValue());
			break;
		case magicalskillboost:
				this.user.getStat().setBonusMagicPower(this.user.getBonusMagicPower()+bonusAttribut.getValue());
				break;
		case parry:
				this.user.getStat().setBonusParry(this.user.getBonusParry()+bonusAttribut.getValue());
				break;
		case block:
				this.user.getStat().setBonusBlock(this.user.getBonusBlock()+bonusAttribut.getValue());
				break;
		case critical:
			if(this.isMainHand==true)
				this.user.getStat().setBonusHandCritRate(this.user.getBonusMainHandCritRate()+bonusAttribut.getValue());
			else
				this.user.getStat().setBonusOffCritRate(this.user.getBonusOffHandCritRate()+bonusAttribut.getValue());
			break;
		case magicalhitaccuracy:
				this.user.getStat().setBonusMagicAccuracy(this.user.getBonusMagicAccuracy()+bonusAttribut.getValue());
				break;
		case magicalresist:
				this.user.getStat().setBonusMagicResist(this.user.getBonusMagicResist()+bonusAttribut.getValue());
				break;
		case attackdelay:
				this.user.getStat().setBonusAttackSpeed(this.user.getBonusAttackSpeed()*(1+bonusAttribut.getValue()));
				break;
		case physicaldefend:
			this.user.getStat().setBonusPhyDef(this.user.getBonusPhyDef()+bonusAttribut.getValue());
			break;
		case speed:
		//	this.user.getStat().setBonusSpeed(this.user.getBonusSpeed()+bonusAttribut.getValue());
			break;
		case elementaldefendfire:
			this.user.getStat().setResistFire(this.user.getResistFire()+bonusAttribut.getValue());
			break;
		case elementaldefendwater:
			this.user.getStat().setResistWater(this.user.getResistWater()+bonusAttribut.getValue());
			break;
		case elementaldefendwind:
			this.user.getStat().setResistWind(this.user.getResistWind()+bonusAttribut.getValue());
			break;	
		case elementaldefendearth:
			this.user.getStat().setResistEarth(this.user.getResistEarth()+bonusAttribut.getValue());
			break;
		default : break;
		}
	}


	public void bonusvisit(Items item){};


	public void bonusvisit(Weapon weapon) {
		try {
			if(this.user.getEquipment().getMainHandItem()==weapon)
			{
				this.isMainHand=true;
			}
} catch (ItemIsNullException e) {}
		int weaponMainHandAttack = (int)(((weapon.getMin_damage()+weapon.getMax_damage())/2)*this.user.getPower()/100.0);
		int mainHandattack = this.user.getMainHandAttack();
		if(this.isMainHand==true)
		{
		    this.user.getStat().setMainHandCritRate(this.user.getMainHandCritRate()-weapon.getPhysical_crit());
		    this.user.getStat().setMainHandAccuracy(this.user.getMainHandAccuracy()-weapon.getHit_accuracy());
		    this.user.getStat().setMainHandAttack(this.user.getMainHandAttack()-(int)(((weapon.getMin_damage()+weapon.getMax_damage())/2)*this.user.getPower()/100.0));
		}
		else
		{
		    this.user.getStat().setOffHandCritRate(this.user.getoffHandCritRate()-weapon.getPhysical_crit());
		    this.user.getStat().setOffHandAccuracy(this.user.getOffHandAccuracy()-weapon.getHit_accuracy());
		    this.user.getStat().setOffHandAttack(this.user.getOffHandAttack()-(int)(((weapon.getMin_damage()+weapon.getMax_damage())/2)*this.user.getPower()/100.0));
		}
		this.user.getStat().setAttackSpeed(weapon.getAttack_speed());
		this.user.getStat().setMagicAccuracy(this.user.getMagicAccuracy()-weapon.getMagic_accuracy());
	    this.user.getStat().setParry(this.user.getParry()-weapon.getParry());
		for(int i=0;i<weapon.getBonusList().size();i++)
			this.removeBonus(weapon.getBonusList().get(i));		
		
	    for(int j=0;j<this.user.getMasteryList().size();j++)
	    {
	      if(this.user.getMasteryList().get(j).getCarac().equals(this.correspondance(weapon.getType())))
	      {
		        int BonustoAdd =(int)(mainHandattack*(this.user.getMasteryList().get(j).getValue()/100.0));
	    	  if(this.isMainHand==true)
	    	  {
	    		  this.user.getStat().setBonusHandAttack(this.user.getBonusMainHandAttack()-BonustoAdd);
	    	  }
	    	  else
	    	  {
		        this.user.getStat().setBonusOffHandAttack(this.user.getBonusOffHandAttack()-BonustoAdd);  
	    	  }
	      }
	    }
	    this.isMainHand=false;
	}


	public void bonusvisit(Armor armor) {
		this.user.getStat().setPhysicalDef(this.user.getPhysicalDef()-armor.getPhysical_def());
		this.user.getStat().setEvasion(this.user.getEvasion()-armor.getEvasion());
		this.user.getStat().setBlock(this.user.getBlock()-armor.getBlock());
		this.user.getStat().setMagicalResist(this.user.getMagicalResist()-armor.getMagical_resist());
		for(int i=0;i<armor.getBonusList().size();i++)
			this.removeBonus(armor.getBonusList().get(i));		
	}


	public void bonusvisit(Manastone mana) {
			this.removeBonus(mana.getBonus());		
	}
	
	private void removeBonus(BonusAttribut bonusAttribut) {
		switch(bonusType.valueOf(bonusAttribut.getCarac()))
		{
		case maxhp:
				this.user.getStat().setBonusHp(this.user.getBonusHp()-bonusAttribut.getValue());
				break;
		case maxmp:
				this.user.getStat().setBonusMp(this.user.getBonusMp()-bonusAttribut.getValue());
				break;
		case hitaccuracy:
			if(this.isMainHand==true)
				this.user.getStat().setBonusHandAccuracy(this.user.getBonusMainHandAccuracy()-bonusAttribut.getValue());
			else
				this.user.getStat().setBonusOffHandAccuracy(this.user.getBonusOffHandAccuracy()-bonusAttribut.getValue());	
			break;
		case dodge:
				this.user.getStat().setBonusEvasion(this.user.getBonusEvasion()-bonusAttribut.getValue());
				break;
		case phyattack:
			if(this.isMainHand==true)
				this.user.getStat().setBonusHandAttack(this.user.getBonusMainHandAttack()-bonusAttribut.getValue());
			else
				this.user.getStat().setBonusOffHandAttack(this.user.getBonusOffHandAttack()-bonusAttribut.getValue());
			break;
		case magicalskillboost:
				this.user.getStat().setBonusMagicPower(this.user.getBonusMagicPower()-bonusAttribut.getValue());
				break;
		case parry:
				this.user.getStat().setBonusParry(this.user.getBonusParry()-bonusAttribut.getValue());
				break;
		case block:
				this.user.getStat().setBonusBlock(this.user.getBonusBlock()-bonusAttribut.getValue());
				break;
		case critical:
			if(isMainHand==true)
				this.user.getStat().setBonusHandCritRate(this.user.getBonusMainHandCritRate()-bonusAttribut.getValue());
			else
				this.user.getStat().setBonusOffCritRate(this.user.getBonusOffHandCritRate()-bonusAttribut.getValue());
			break;
		case magicalhitaccuracy:
				this.user.getStat().setBonusMagicAccuracy(this.user.getBonusMagicAccuracy()-bonusAttribut.getValue());
				break;
		case magicalresist:
				this.user.getStat().setBonusMagicResist(this.user.getBonusMagicResist()-bonusAttribut.getValue());
				break;
		case attackdelay:
				this.user.getStat().setBonusAttackSpeed(this.user.getBonusAttackSpeed()*(1-bonusAttribut.getValue()));
				break;
		case physicaldefend:
			this.user.getStat().setBonusPhyDef(this.user.getBonusPhyDef()-bonusAttribut.getValue());
			break;
		case speed:
		//	this.user.getStat().setBonusSpeed(this.user.getBonusSpeed()+bonusAttribut.getValue());
			break;
		case elementaldefendfire:
			this.user.getStat().setResistFire(this.user.getResistFire()-bonusAttribut.getValue());
			break;
		case elementaldefendwater:
			this.user.getStat().setResistWater(this.user.getResistWater()-bonusAttribut.getValue());
			break;
		case elementaldefendwind:
			this.user.getStat().setResistWind(this.user.getResistWind()-bonusAttribut.getValue());
			break;	
		case elementaldefendearth:
			this.user.getStat().setResistEarth(this.user.getResistEarth()-bonusAttribut.getValue());
			break;
		default : break;
		}
	}


	public void bonusvisit(Accessory accessory) {
		this.user.getStat().setMagicalResist(this.user.getMagicalResist()-accessory.getMagical_resist());
		for(int i=0;i<accessory.getBonusList().size();i++)
			this.removeBonus(accessory.getBonusList().get(i));		
	}

	private String correspondance(WeaponsType type)
	{
		switch(type)
		{
		case one_h_sword:
				return "sword";
		case one_h_dagger:
				return "dagger";
		case one_h_mace:
				return "mace";
		case two_h_sword:
				return "2hsword";
		case two_h_polearm:
				return "polearm";
		case bow:
				return "bow";
		case two_h_orb:
				return "orb";
		case two_h_book:
				return "book";
			
		default :
				return "Undefined weapon";
		
		}
	}

	public void DetermineAtkSpeed(Weapon wep) throws ItemIsNullException
	{
		if(this.isMainHand==true)
		{
			if(this.user.getEquipment().getOffHandItem()==null)
			{
				this.user.getStat().setAttackSpeed(wep.getAttack_speed());
			}
		}
	}
	
}
